/**
 * 缠论JS工具
 */
import deepClone from "./deepClone.js";

class CLCandlestick {
    constructor(lines=[], options={}) {
        this.lines = lines || [] //原始K线数据
        this.pageSize = options.pageSize// 数据量
        this.isJoinShow = options.isJoinShow // 是否合并
        this.isLineShow = options.isLineShow
        this.isSegmentShow = options.isSegmentShow
    }

    getLineLength() {
        return !!this.pageSize ? (this.lines.length > this.pageSize ? this.lines.length - this.pageSize : 0) : 0
    }

    klineToLines(list, len){
        return list.slice(len).map(item => {
            if(item.o) {
                // 期货
                return  {
                    date: item.d,
                    open: Number(item.o),
                    close: Number(item.c),
                    lowest: Number(item.l),
                    highest: Number(item.h),
                    volume: Number(item.v),
                    num: 1,
                    left: 0,
                    right: 0
                }
            }
            const oldStart = item.split(',').map((item, i) => {
                return i !== 0 ? Number(item) : item
            });
            return {
                date: oldStart[0],
                open: oldStart[1],
                close: oldStart[2],
                lowest: oldStart[4],
                highest: oldStart[3],
                volume: oldStart[5],
                num: 1,
                left: 0,
                right: 0
            }
        });
    }

    getJoinData(list){
        // 合并后的数据
        let newArr = []

        const joinLines = (oldArr, newArr) => {
            if (oldArr.length === 0) {
                return false;
            }

            // 当前K线信息
            const currentLine = deepClone(oldArr[0])

            // 包含关系计算
            if (newArr.length === 0) {
                // 初始化第一条K线
                newArr.push({
                    date: currentLine.date,
                    lowest: currentLine.lowest,
                    highest: currentLine.highest,
                    num: currentLine.num,
                    left: currentLine.left,
                    right: currentLine.right
                })
            } else {
                //合并后的新K线最后一条数据
                const newCurrentLine = deepClone(newArr[newArr.length - 1])
                //合并后的新K线倒数第二数据
                const preNewCurrentLine = deepClone(newArr[newArr.length - 2])

                if (newCurrentLine.highest >= currentLine.highest && newCurrentLine.lowest <= currentLine.lowest) {
                    // 判断左包右
                    if (newArr.length > 1 && newCurrentLine.highest > preNewCurrentLine.highest) {
                        // 方向向上处理
                        newArr.push({
                            date: newCurrentLine.date,
                            lowest: currentLine.lowest,
                            highest: newCurrentLine.highest,
                            num: newCurrentLine.num + 1,
                            left: newCurrentLine.left,
                            right: newCurrentLine.right +1
                        })
                        newArr.splice(newArr.length - 2, 1);
                    } else {
                        // 起点方向不明 或 方向向下  都向下处理
                        newArr.push({
                            date: newCurrentLine.date,
                            lowest: newCurrentLine.lowest,
                            highest: currentLine.highest,
                            num: newCurrentLine.num + 1,
                            left: newCurrentLine.left,
                            right: newCurrentLine.right +1
                        })
                        newArr.splice(newArr.length - 2, 1);
                    }

                } else if (newCurrentLine.highest <= currentLine.highest && newCurrentLine.lowest >= currentLine.lowest) {
                    // 判断右包左
                    if (newArr.length > 1 && newCurrentLine.highest > preNewCurrentLine.highest) {
                        // 向上处理
                        newArr.push({
                            date: currentLine.date,
                            lowest: newCurrentLine.lowest,
                            highest: currentLine.highest,
                            num: newCurrentLine.num + 1,
                            left: newCurrentLine.left + 1,
                            right: newCurrentLine.right
                        })
                        newArr.splice(newArr.length - 2, 1);
                    } else {
                        // 向下处理
                        newArr.push({
                            date: currentLine.date,
                            lowest: currentLine.lowest,
                            highest: newCurrentLine.highest,
                            num: newCurrentLine.num + 1,
                            left: newCurrentLine.left + 1,
                            right: newCurrentLine.right
                        })
                        newArr.splice(newArr.length - 2, 1);
                    }

                } else {
                    // 不包含
                    newArr.push({
                        date: currentLine.date,
                        lowest: currentLine.lowest,
                        highest: currentLine.highest,
                        num: currentLine.num,
                        left: currentLine.left,
                        right: currentLine.right
                    })
                }
            }

            joinLines(oldArr.slice(1), newArr);

        }

        joinLines(list, newArr)

        return newArr
    }

    getUpDownData(list){
        for (let i = 0; i < list.length; i++) {
            if (i === 0 || i === list.length - 1) {
                list[i].upDown = 'none'
            }
            if (list[i + 1] && list[i + 2]) {
                if (
                    list[i + 1].lowest > list[i].lowest &&
                    list[i + 1].lowest > list[i + 2].lowest &&
                    list[i + 1].highest > list[i].highest &&
                    list[i + 1].highest > list[i + 2].highest
                ) {
                    // 顶型
                    list[i + 1].upDown = 'up'
                    list[i + 1].value = list[i + 1].highest
                } else if (
                    list[i + 1].lowest < list[i].lowest &&
                    list[i + 1].lowest < list[i + 2].lowest &&
                    list[i + 1].highest < list[i].highest &&
                    list[i + 1].highest < list[i + 2].highest
                ) {
                    // 底型
                    list[i + 1].upDown = 'down'
                    list[i + 1].value = list[i + 1].lowest
                } else {
                    list[i + 1].upDown = 'none'
                }
            }
        }
        return list
    }

    getLineData(list){
        for (let i = 0; i < list.length; i++) {
            if (list[i].upDown === 'up' || list[i].upDown === 'down') {
                // 确定起点
                list[i].line = true
                // 查找终点
                let j = i + 2
                // 是否有下一节点
                let flag = false

                for (j; j < list.length; j++) {
                    if (list[j].upDown === 'up' || list[j].upDown === 'down') {
                        if (list[i].upDown === 'down' && list[j].upDown === 'down') {
                            // 底底型
                            if (list[j].lowest < list[i].lowest) {
                                list[i].line = false
                                list[j].line = true
                                i = j - 1;
                                flag = true
                                break
                            }
                        } else if (list[i].upDown === 'up' && list[j].upDown === 'up') {
                            // 顶顶型
                            if (list[j].highest > list[i].highest) {
                                list[i].line = false
                                list[j].line = true
                                i = j - 1;
                                flag = true
                                break
                            }
                        } else {
                            //顶底型
                            // 新笔
                            // 分型不能互相共用K线
                            if(j-i > 3 || j-i === 3 && list[i].right + list[i + 1].num +  list[j - 1].num + list[j].left >= 3) {
                                if (
                                    (list[i].upDown === 'down' && list[j].upDown === 'up' && list[j].highest >  list[i].highest)
                                    ||
                                    (list[i].upDown === 'up' && list[j].upDown === 'down' && list[i].highest >  list[j].highest)
                                ) {
                                    list[j].line = true
                                    i = j - 1;
                                    flag = true
                                    break
                                }
                            }
                        }
                    }
                }

                if(!flag) {
                    // 没有下个节点 终止查找
                    break
                }
            }
        }

        return list
    }
    isLineOverlap(lines, i){
        return lines[i].upDown === 'up' ? lines[i+3].value < lines[i+1].value : lines[i+3].value > lines[i+1].value
    }

    getKLinePData(list){
        let lines = list.filter(item => item.line);

        let upArr = []
        let downArr = []
        let start = 0 // 起点
        let startUpDown = '' // 起点up down
        for(let i=0; i<lines.length; i++) {
            if(lines[i+3] && this.isLineOverlap(lines, i)) {
                start = i
                startUpDown = lines[i].upDown
                break
            }
        }

        for(let i=0; i<lines.length; i++) {
            if(lines[i+1]) {
                if(lines[i].upDown === 'up'){
                    // 向下线段特征提取
                    downArr.push({
                        index: i,
                        lowest: lines[i+1].value,
                        highest: lines[i].value,
                        date: lines[i].date,
                        upDown: 'down'

                    })
                } else {
                    // 向上线段特征提取
                    upArr.push({
                        index: i,
                        lowest: lines[i].value,
                        highest: lines[i+1].value,
                        date: lines[i].date,
                        upDown: 'up'

                    })
                }
            }
        }


        let upJoinArr = this.getUpDownData(this.getJoinData(upArr)).filter(item => item.upDown === 'down')
        let downJoinArr = this.getUpDownData(this.getJoinData(downArr)).filter(item => item.upDown === 'up')


        let allArr = [lines[start], ...upJoinArr, ...downJoinArr].sort((a, b) => {
            return new Date(a.date).getTime() - new Date(b.date).getTime()
        })


        const filterArr = (allArr) => {
            for(let i=0; i< allArr.length; i++) {
                if(allArr[i+1]) {
                    if(allArr[i].upDown === 'up' && allArr[i+1].upDown === 'up') {
                        if(allArr[i].value <= allArr[i+1].value) {
                            allArr[i+1].isP = true
                            allArr[i].isP = false
                        } else {
                            allArr[i+1].isP = false
                            allArr[i].isP = true
                        }
                        i++
                    } else if(allArr[i].upDown === 'down' && allArr[i+1].upDown === 'down') {
                        if(allArr[i].value <= allArr[i+1].value) {
                            allArr[i].isP = true
                            allArr[i+1].isP = false
                        } else {
                            allArr[i].isP = false
                            allArr[i+1].isP = true
                        }
                        i++
                    } else {
                        if(
                            allArr[i].upDown === 'up' && allArr[i+1].upDown === 'down' && allArr[i].value > allArr[i+1].value
                            ||
                            allArr[i].upDown === 'down' && allArr[i+1].upDown === 'up' && allArr[i].value < allArr[i+1].value
                        ) {
                            allArr[i].isP = true
                            allArr[i+1].isP = true
                        } else {
                            allArr[i+1].isP = false
                            i++
                        }
                    }
                } else {
                    allArr[i].isP = true
                }
            }

            return deepClone(allArr)
        }



        allArr = filterArr(allArr)


        let newArr = allArr.filter(item => item?.isP)


        // 线段与笔重合 形成古怪线段
        // for (let i=0; i< lines.length; i++) {
        //     for (let j = 0; j<newArr.length; j++) {
        //
        //         if(lines[i+1] && newArr[j+1] && lines[i].date === newArr[j].date && lines[i+1].date === newArr[j+1].date) {
        //             if(newArr[j+1].upDown === 'up') {
        //                 newArr[j].isP = false
        //             } else {
        //                 newArr[j+1].isP = false
        //             }
        //         }
        //     }
        // }

        newArr = filterArr(newArr.filter(item => item?.isP))


        return newArr.filter(item => item?.isP);
    }

    getMacdData(list){
        let data = list.map(item => {
            if(item.o){
                return [item.o, item.c, item.l, item.h]
            }
            const oldStart = item.split(',').map((item, i) => {
                return i !== 0 ? Number(item) : item
            });
            return [oldStart[1], oldStart[2],oldStart[4],oldStart[3]];
        })

        const build_diff_data = (m_short, m_long, data) => {
            let result = [];
            let pre_emashort = 0;
            let pre_emalong = 0;
            for(let i=0; i< data.length; i++){
                let ema_short = data[i][1];
                let ema_long = data[i][1];
                if(i !== 0){
                    ema_short = ((pre_emashort * 11 + ema_short * 2) / 13).toFixed(3);
                    ema_long = ((pre_emalong * 25 + ema_long * 2) / 27).toFixed(3);
                }
                pre_emashort = ema_short;
                pre_emalong = ema_long;

                result.push((ema_short- ema_long).toFixed(3));
            }
            return result;
        }

        let diff = build_diff_data(12, 26, data);

        const build_dea_data = (m, diff) => {
            let result = [];
            let pre_ema_diff = 0;
            for(let i=0; i<diff.length; i++){
                let ema_diff = diff[i];
                if(i !== 0){
                    ema_diff = ((pre_ema_diff * 8 + ema_diff * 2)/10).toFixed(3);
                }
                pre_ema_diff = ema_diff;
                result.push(ema_diff);
            }
            return result;
        }

        let dea = build_dea_data(9, diff);

        const build_macd_data = (data, diff, dea) => {
            let result = [];
            for(let i=0; i<data.length; i++){
                result.push((2*(diff[i] - dea[i])).toFixed(3));
            }
            return result;
        }
        return {
            macd: build_macd_data(data, diff, dea),
            diff,
            dea
        }
    }



    getData(){
        let len = this.getLineLength()

        const macdData = this.getMacdData(this.lines)
        const macd = macdData.macd.slice(len).map((item, index) => {
            return [index, item, item > 0 ? 1 : -1]
        })
        const diff = macdData.diff.slice(len)
        const dea = macdData.dea.slice(len)

        let xAxisData = []
        let newklines = []
        const volumes = []

        const xAxisAllData = []

        let klines = this.klineToLines(this.lines, len)
        //合并后的K线数据
        let joinKlines = this.getJoinData(klines)
        //标记有顶底的数据
        const upDownData = this.getUpDownData(joinKlines)
        // 标记有笔的数据
        let lineData = this.getLineData(upDownData)

        // 线段数据
        let lineSegmentData = this.getKLinePData(lineData)


        klines.forEach((item, index) => {
            xAxisData.push(item.date)
            xAxisAllData.push(item.date)

            newklines.push([item.open, item.close, item.lowest, item.highest])

            volumes.push([index, item.volume, item.open < item.close ? 1 : -1])
        });


        if(this.isJoinShow) {
            xAxisData = []
            newklines = []
            joinKlines.forEach((item, index) => {
                xAxisData.push(item.date)
                newklines.push({
                        value: [item.lowest, item.highest, item.lowest, item.highest],
                        itemStyle: {
                            color: item.num > 1 ? '#999':'#ccc',
                            color0: item.num > 1 ? '#999':'#ccc',
                            borderColor: item.num > 1 ? '#999':'#ccc',
                            borderColor0: item.num > 1 ? '#999':'#ccc'
                        },
                    }

                )
            });
        }

        if(!this.isLineShow) {
            lineData = []
        }

        if(!this.isSegmentShow) {
            lineSegmentData = []
        }



        return {
            macd,
            diff,
            dea,
            upDownData,
            lineData,
            xAxisData,
            newklines,
            volumes,
            xAxisAllData,
            lineSegmentData
        }
    }

}

export default CLCandlestick